////////////////////////////////////////
//	File Header
//	Author: Stephen Ringer | stephen.ringer@gmail.com
//	File: SAGAD3DSkinManager.h
//
////////////////////////////////////////

////////////////////////////////////////
//	File Includes
#include "SAGAD3DSkinManager.h" //	class definition
#include "SAGA.h"				//	return values
#include "d3dx9.h"				//	Shaderz
//
////////////////////////////////////////

////////////////////////////////////////
//	External Global Variable
//		For Log Flush checking
extern bool g_bLF;
//
////////////////////////////////////////

////////////////////////////////////////
//	Macros
#define RGB16BIT(r,g,b) ((b%32) + ((g%64) << 5) + ((r%32) << 11))
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: SAGAD3DSkinManager
//	Purpose:
//		Constructor: Initializes vertex cache arrays
SAGAD3DSkinManager::SAGAD3DSkinManager(LPDIRECT3DDEVICE9 pDevice, FILE *pLog) 
{
	m_nNumMaterials = 0;
	m_nNumTextures  = 0;
	m_nNumSkins     = 0;
	m_pMaterials    = NULL;
	m_pTextures     = NULL;
	m_pSkins        = NULL;
	m_pLog          = pLog;
	m_pDevice       = pDevice;
	Log("Online");
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: ~SAGAD3DSkinManager
//	Purpose:
//		Destructor: Release vertex cache arrays
SAGAD3DSkinManager::~SAGAD3DSkinManager()
{
	//	Release Direct3D Texture objects
	for(UINT nTexts = 0; nTexts < m_nNumTextures; nTexts++)
	{
		if(m_pTextures[nTexts].pData)
		{
			((LPDIRECT3DTEXTURE9)(m_pTextures[nTexts].pData))->Release();
			m_pTextures[nTexts].pData = NULL;
		}
		if(m_pTextures[nTexts].pClrKeys)
		{
			delete [] m_pTextures[nTexts].pClrKeys;
			m_pTextures[nTexts].pClrKeys = NULL;
		}
		if(m_pTextures[nTexts].chName)
		{
			delete [] m_pTextures[nTexts].chName;
			m_pTextures[nTexts].chName = NULL;
		}
	}
	//	Free Memory
	if(m_pTextures)
	{
		free(m_pTextures);
		m_pTextures = NULL;
	}
	if(m_pMaterials)
	{
		free(m_pMaterials);
		m_pMaterials = NULL;
	}
	if(m_pSkins)
	{
		free(m_pSkins);
		m_pSkins = NULL;
	}
	Log("Offline - ok");
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: Reset
void SAGAD3DSkinManager::Reset() 
{
	//	Release direct3d texture objects
	for (UINT i=0; i<m_nNumTextures; i++) 
	{
		if (m_pTextures[i].pData) 
		{
			((LPDIRECT3DTEXTURE9)(m_pTextures[i].pData))->Release();
			m_pTextures[i].pData = NULL;
		}
		if (m_pTextures[i].pClrKeys) 
		{
			delete [] m_pTextures[i].pClrKeys;
			m_pTextures[i].pClrKeys = NULL;
		}
		if (m_pTextures[i].chName) 
		{
			delete [] m_pTextures[i].chName;
			m_pTextures[i].chName = NULL;
		}
	}

	//	Release allocated memory
	if (m_pMaterials) 
	{
		free(m_pMaterials);
		m_pMaterials = NULL;
	}
	if (m_pTextures) 
	{
		free(m_pTextures);
		m_pTextures = NULL;
	}
	if (m_pSkins) 
	{
		free(m_pSkins);
		m_pSkins = NULL;
	}

	m_nNumMaterials = 0;
	m_nNumTextures  = 0;
	m_nNumSkins     = 0;
	m_pMaterials    = NULL;
	m_pTextures     = NULL;
	m_pSkins        = NULL;
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: ColorEqual
//	Purpose:
//		Compares if two color values are equal
inline bool SAGAD3DSkinManager::ColorEqual(const SAGACOLOR *pCol0, const SAGACOLOR *pCol1)
{
	if(pCol0->fA != pCol1->fA) return false;
	if(pCol0->fR != pCol1->fR) return false;
	if(pCol0->fG != pCol1->fG) return false;
	if(pCol0->fB != pCol1->fB) return false;
	return true;
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: MaterialEqual
//	Purpose:
//		Compares if two material values are equal
bool SAGAD3DSkinManager::MaterialEqual(const SAGAMATERIAL *pMat0, const SAGAMATERIAL *pMat1)
{
	if(!ColorEqual(&pMat0->cAmbient,	&pMat1->cAmbient))	return false;
	if(!ColorEqual(&pMat0->cDiffuse,	&pMat1->cDiffuse))	return false;
	if(!ColorEqual(&pMat0->cEmissive,	&pMat1->cEmissive)) return false;
	if(!ColorEqual(&pMat0->cSpecular,	&pMat1->cSpecular)) return false;
	if(pMat0->fPower != pMat1->fPower) return false;
	return true;
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: GetSkin
//	Purpose:
//		To retrieve a skin from the manager
SAGASKIN SAGAD3DSkinManager::GetSkin(UINT nSkinID)
{ 
	SAGASKIN skin;
	if(nSkinID < m_nNumSkins) 
		memcpy(&skin, &m_pSkins[nSkinID], sizeof(SAGASKIN));
	return skin;
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: GetMaterial
//	Purpose:
//		To retrieve a material from the manager
SAGAMATERIAL SAGAD3DSkinManager::GetMaterial(UINT nMatID)
{ 
	SAGAMATERIAL mat;
	if(nMatID < m_nNumMaterials) 
		memcpy(&mat, &m_pMaterials[nMatID], sizeof(SAGAMATERIAL));
	return mat;
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: GetTextureName
//	Purpose:
//		To retrieve the name of the file a given texture resides
const char* SAGAD3DSkinManager::GetTextureName(UINT nID, float *pfAlpha, SAGACOLOR *pAK, UCHAR *pNum)
{
	if(nID >= m_nNumTextures) return NULL;
	if(pfAlpha) *pfAlpha = m_pTextures[nID].fAlpha;
	if(pNum) *pNum = m_pTextures[nID].dwNum;
	if(m_pTextures[nID].pClrKeys && pAK)
		memcpy(pAK, m_pTextures[nID].pClrKeys, sizeof(SAGACOLOR) * m_pTextures[nID].dwNum);
	return m_pTextures[nID].chName;
}
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: AddSkin
//	Purpose:
//		Creates a new skin object using the given material. texture is
//		set to NULL till a texture is added to that skin.
//		Parameters:
//			ZFXCOLOR - ambient material  color 
//			ZFXCOLOR - diffuse material color 
//	        ZFXCOLOR - emissive material color 
//			ZFXCOLOR - specular material color 
//			float    - power for specular reflection
//		Returns:
//			UINT     - ID to reference new skin
HRESULT SAGAD3DSkinManager::AddSkin(const SAGACOLOR *pcAmbient, const SAGACOLOR *pcDiffuse,
								   const SAGACOLOR *pcEmissive, const SAGACOLOR *pcSpecular,
								   float fSpecPower, UINT  *nSkinID) 
{
	UINT    nMat, n;
	bool    bMat = false;

	//	Allocate 50 new memory slots for skins if necessary
	if ( (m_nNumSkins%50) == 0 ) 
	{
	   n = (m_nNumSkins+50)*sizeof(SAGASKIN);
	   m_pSkins = (SAGASKIN*)realloc(m_pSkins, n);
	   if (!m_pSkins) return SAGA_OUTOFMEMORY;
	}

	SAGAMATERIAL mat;
	mat.cAmbient  = *pcAmbient;
	mat.cDiffuse  = *pcDiffuse;
	mat.cEmissive = *pcEmissive;
	mat.cSpecular = *pcSpecular;
	mat.fPower    = fSpecPower;

	//	Do we already have an equal material
	for (nMat=0; nMat<m_nNumMaterials; nMat++) 
	{
	   if ( MaterialEqual(&mat, &m_pMaterials[nMat]) ) 
	   {
		   bMat = true;
		   break;
	   }
	}

	//	If exists store its ID otherwise create it new
	if (bMat) m_pSkins[m_nNumSkins].nMaterial = nMat;
	else 
	{
	   m_pSkins[m_nNumSkins].nMaterial = m_nNumMaterials;

	   //	Allocate 50 new memory slots for materials if necessary
	   if ( (m_nNumMaterials%50) == 0 ) 
	   {
		   n = (m_nNumMaterials+50)*sizeof(SAGAMATERIAL);
		   m_pMaterials = (SAGAMATERIAL*)realloc(m_pMaterials, n);
		   if (!m_pMaterials) return SAGA_OUTOFMEMORY;
	   }
	   memcpy(&m_pMaterials[m_nNumMaterials], &mat, sizeof(SAGAMATERIAL));
	   m_nNumMaterials++;
	}

	m_pSkins[m_nNumSkins].bAlpha = false;
	for (int i=0; i<8; i++) m_pSkins[m_nNumSkins].nTexture[i] = MAX_ID;

	//	Save ID and add to count
	(*nSkinID) = m_nNumSkins;
	m_nNumSkins++;

	return SAGA_OK;
} 
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: AddTexture
//	Purpose:
//		Add a texture to a given skin. BMP is only loaded if not used yet.
//		Can also be used to set alpha channels on textures for alpha keys
//		and/or overall transparency. Set bool to true in both cases.
//		Parameters:
//			UINT      - ID of skin to receive texture
//			char*     - name of 24 bit BMP file used as texture
//			bool      - use alphablending?
//			float     - value for overall transparency
//			SAGACOLOR* - array of RGB values to receive their A value
//			DWORD     - number of colors in array
HRESULT SAGAD3DSkinManager::AddTexture(UINT nSkinID, const char *chName, bool bAlpha, float fAlpha,
									  SAGACOLOR *cColorKeys, DWORD dwNumColorKeys) 
{
	SAGATEXTURE *pSAGATex=NULL;
	HRESULT hr;
	UINT nTex, n;
	bool bTex=false;

	//	Is skin ID valid at all
	if (nSkinID >= m_nNumSkins) return SAGA_INVALIDID;

	//	All 8 stages for this skin already set?
	if (m_pSkins[nSkinID].nTexture[7] != MAX_ID) 
	{
		Log("Error: AddTexture() failed, all 8 stages set");
		return SAGA_BUFFERSIZE;
	}

	//	Do we already have this texture
	for (nTex=0; nTex<m_nNumTextures; nTex++) 
	{
		if ( strcmp(chName, m_pTextures[nTex].chName) == 0 ) 
		{
			bTex = true;
			break;
		}
	}

	//	Load new texture if not yet done
	if (!bTex) 
	{
		//	Allocate 50 new memory slots for textures if necessary
		if ( (m_nNumTextures%50) == 0 ) 
		{
			n = (m_nNumTextures+50)*sizeof(SAGATEXTURE);
			m_pTextures = (SAGATEXTURE*)realloc(m_pTextures, n);
			if (!m_pTextures) 
			{
			  Log("Error: AddTexture() failed, realloc()");
			  return SAGA_OUTOFMEMORY;
			}
		}

		//	We use AlphaBlending at least from now on
		if (bAlpha) m_pSkins[nSkinID].bAlpha = true;
		else m_pTextures[m_nNumTextures].fAlpha = 1.0f;

		m_pTextures[m_nNumTextures].pClrKeys = NULL;

		//	Save texture name
		m_pTextures[m_nNumTextures].chName = new char[strlen(chName)+1];
		memcpy(m_pTextures[m_nNumTextures].chName, chName, strlen(chName)+1);

		//	Create D3D texture from that pointer
		hr = CreateTexture(&m_pTextures[m_nNumTextures], bAlpha);
		if (FAILED(hr)) 
		{
			Log("Error: CreateTexture() failed");
			return hr;
		}

		//	Add alpha values if needed
		if (bAlpha) 
		{

		pSAGATex = &m_pTextures[m_nNumTextures];

		//	Remind information
		pSAGATex->dwNum = dwNumColorKeys;
		pSAGATex->pClrKeys = new SAGACOLOR[dwNumColorKeys];
		memcpy(pSAGATex->pClrKeys, cColorKeys, 
		  sizeof(SAGACOLOR)*pSAGATex->dwNum);

		LPDIRECT3DTEXTURE9 pTex = (LPDIRECT3DTEXTURE9)pSAGATex->pData;

		//	Set alpha keys first
		for (DWORD dw=0; dw<dwNumColorKeys; dw++) 
		{
			hr = SetAlphaKey(&pTex,
			  UCHAR(cColorKeys[dw].fR*255),
			  UCHAR(cColorKeys[dw].fG*255),
			  UCHAR(cColorKeys[dw].fB*255),
			  UCHAR(cColorKeys[dw].fA*255));
			if (FAILED(hr)) 
			{
				Log("Error: SetAlphaKey() failed");
				return hr;
			}
		}

		if (fAlpha < 1.0f) 
		{
			//	Remind that value for info purpose
			pSAGATex->fAlpha = fAlpha;

			//	Now general transparency
			hr = SetTransparency(&pTex, UCHAR(fAlpha*255));
			if (FAILED(hr)) 
			{
				Log("Error: SetTransparency() failed");
				return hr;
			}
		}
	}


		//	Save ID and add to count
		nTex = m_nNumTextures;
		m_nNumTextures++;
	}

	//	Put texture ID to skin ID
	for (int i=0; i<8; i++) 
	{
		if (m_pSkins[nSkinID].nTexture[i] == MAX_ID) 
		{
			m_pSkins[nSkinID].nTexture[i] = nTex;
			break;
		}
	}
	return SAGA_OK;
} 
//
////////////////////////////////////////


////////////////////////////////////////
//	Function Name: AddTextureHeightmapAsBump
//	Purpose:
//		Add a texture to a given skin, but this is supposed to be a
//		heightmap that should be recalculated as normal map needed
//		to perform bump mapping. 
HRESULT SAGAD3DSkinManager::AddTextureHeightmapAsBump( UINT nSkinID, const char *chName) 
{
	SAGATEXTURE *pSAGATex=NULL;
	HRESULT     hr;
	UINT        nTex, n;
	bool        bTex=false;

	//	Is skin ID valid at all
	if (nSkinID >= m_nNumSkins) return SAGA_INVALIDID;

	//	All 8 stages for this skin already set?
	if (m_pSkins[nSkinID].nTexture[7] != MAX_ID) 
	{
		Log("Error: AddTexture() failed, all 8 stages set");
		return SAGA_BUFFERSIZE;
	}

	//	Do we already have this texture
	for (nTex=0; nTex<m_nNumTextures; nTex++) 
	{
		if ( strcmp(chName, m_pTextures[nTex].chName) == 0 ) 
		{
			bTex = true;
			break;
		}
	}

	//	Load new texture if not yet done
	if (!bTex) 
	{
		//	Allocate 50 new memory slots for textures if necessary
		if ( (m_nNumTextures%50) == 0 ) 
		{
			n = (m_nNumTextures+50)*sizeof(SAGATEXTURE);
			m_pTextures = (SAGATEXTURE*)realloc(m_pTextures, n);
			if (!m_pTextures) 
			{
				Log("Error: AddTexture() failed, realloc()");
				return SAGA_OUTOFMEMORY;
			}
		}

		//	No alpha blending needed
		m_pTextures[m_nNumTextures].fAlpha = 1.0f;
		m_pTextures[m_nNumTextures].pClrKeys = NULL;

		//	Save texture name
		m_pTextures[m_nNumTextures].chName = new char[strlen(chName)+1];
		memcpy(m_pTextures[m_nNumTextures].chName, chName, strlen(chName)+1);

		//	Create D3D texture from that pointer
		hr = CreateTexture(&m_pTextures[m_nNumTextures], true);
		if (FAILED(hr)) 
		{
			Log("Error: CreateTexture() failed");
			return hr;
		}

		//	Build normals from HeightValues
		hr = ConvertToNormalmap( &m_pTextures[m_nNumTextures] );
		if (FAILED(hr)) 
		{
			Log("Error: ConvertToNormalmap() failed");
			return hr;
		}

		//	Save ID and add to count
		nTex = m_nNumTextures;
		m_nNumTextures++;
	} 

	//	Put texture ID to skin ID
	for (int i=0; i<8; i++) 
	{
		if (m_pSkins[nSkinID].nTexture[i] == MAX_ID) 
		{
			m_pSkins[nSkinID].nTexture[i] = nTex;
			break;
		}
	}

	return SAGA_OK;
}
//
////////////////////////////////////////


////////////////////////////////////////
//	Function Name: ExchangeTexture
//	Purpose:
//		This method is used to exchange a texture in a given skin at the
//		given stage. 
HRESULT SAGAD3DSkinManager::ExchangeTexture(UINT nSkinID, UINT nTexStage, const char *chName, 
											bool bAlpha, float fAlpha, SAGACOLOR *cColorKeys, 
											DWORD dwNumColorKeys) 
{
	SAGATEXTURE *pSAGATex=NULL;
	HRESULT     hr;
	UINT        nTex, n;
	bool        bTex=false;

	//	Is skin ID valid at all
	if (nSkinID >= m_nNumSkins) return SAGA_INVALIDID;

	//	We can only take 7 textures for one skin
	if (nTexStage > 7) return SAGA_BUFFERSIZE;

	//	Clear out texture
	if (!chName) 
	{
	   m_pSkins[nSkinID].nTexture[nTexStage] = MAX_ID;
	   return SAGA_OK;
	}

	//	Do we already have this texture
	for (nTex=0; nTex<m_nNumTextures; nTex++) 
	{
	   if ( strcmp(chName, m_pTextures[nTex].chName) == 0 ) 
	   {
		   bTex = true;
		   break;
	   }
	} 

	//	Load new texture if not yet done
	if (!bTex) 
	{
	   //	Allocate 50 new memory slots for textures if necessary
	   if ( (m_nNumTextures%50) == 0 ) 
	   {
		   n = (m_nNumTextures+50)*sizeof(SAGATEXTURE);
		   m_pTextures = (SAGATEXTURE*)realloc(m_pTextures, n);
		   if (!m_pTextures) return SAGA_OUTOFMEMORY;
	   }

	   //	We use AlphaBlending at least from now on
	   if (bAlpha) m_pSkins[nSkinID].bAlpha = true;
	   else m_pTextures[m_nNumTextures].fAlpha = 1.0f;

	   m_pTextures[m_nNumTextures].pClrKeys = NULL;

	   //	Save texture name
	   m_pTextures[m_nNumTextures].chName = new char[strlen(chName)+1];
	   memcpy(m_pTextures[m_nNumTextures].chName, chName, strlen(chName)+1);

	   //	Create d3d texture from that pointer
	   hr = CreateTexture(&m_pTextures[m_nNumTextures], bAlpha);
	   if (FAILED(hr)) 
	   {
		   Log("Error: CreateTexture() failed");
		   return hr;
	   }

	   //	Add alpha values if needed
	   if (bAlpha) 
	   {

		   pSAGATex = &m_pTextures[m_nNumTextures];

		   //	Remind information
		   pSAGATex->dwNum = dwNumColorKeys;
		   pSAGATex->pClrKeys = new SAGACOLOR[dwNumColorKeys];
		   memcpy(pSAGATex->pClrKeys, cColorKeys, 
			   sizeof(SAGACOLOR)*pSAGATex->dwNum);

		   LPDIRECT3DTEXTURE9 pTex = (LPDIRECT3DTEXTURE9)pSAGATex->pData;

		   //	Set alpha keys first
		   for (DWORD dw=0; dw<dwNumColorKeys; dw++) 
		   {
			   hr = SetAlphaKey(&pTex,
				   UCHAR(cColorKeys[dw].fR*255),
				   UCHAR(cColorKeys[dw].fG*255),
				   UCHAR(cColorKeys[dw].fB*255),
				   UCHAR(cColorKeys[dw].fA*255));
			   if (FAILED(hr)) 
			   {
				   Log("Error: SetAlphaKey() failed");
				   return hr;
			   }
		   }

		   if (fAlpha < 1.0f) 
		   {
			   //	Remind that value for info purpose
			   pSAGATex->fAlpha = fAlpha;

			   //	Now generell transparency
			   hr = SetTransparency(&pTex, UCHAR(fAlpha*255));
			   if (FAILED(hr)) 
			   {
				   Log("Error: SetTransparency() failed");
				   return hr;
			   }
		   }
	   }

	   //	Save ID and add to count
	   nTex = m_nNumTextures;
	   m_nNumTextures++;
	}

	//	Put texture ID to skin ID
	m_pSkins[nSkinID].nTexture[nTexStage] = nTex;
	return SAGA_OK;
} 
//
////////////////////////////////////////


////////////////////////////////////////
//	Function Name: ExchangeMaterial
//	Purpose:
//		This method lets you exchange the material used in the given skin. 
HRESULT SAGAD3DSkinManager::ExchangeMaterial(	UINT nSkinID, const SAGACOLOR *pcAmbient, 
												const SAGACOLOR *pcDiffuse,const SAGACOLOR *pcEmissive, 
												const SAGACOLOR *pcSpecular, float fSpecPower) 
{
	UINT nMat=0, n=0;
	bool bMat=false;

	//	Is skin ID valid at all
	if (nSkinID >= m_nNumSkins) return SAGA_INVALIDID;

	SAGAMATERIAL mat;
	mat.cAmbient  = *pcAmbient;
	mat.cDiffuse  = *pcDiffuse;
	mat.cEmissive = *pcEmissive;
	mat.cSpecular = *pcSpecular;
	mat.fPower    = fSpecPower;

	//	Do we already have an equal material
	for (nMat=0; nMat<m_nNumMaterials; nMat++) 
	{
		if ( MaterialEqual(&mat, &m_pMaterials[nMat]) ) 
		{
			bMat = true;
			break;
		}
	} 

	//	If exists store its ID otherwise create it new
	if (bMat) m_pSkins[nSkinID].nMaterial = nMat;
	//	Else make a new material accordingly
	else 
	{
		m_pSkins[nSkinID].nMaterial = m_nNumMaterials;

		//	Allocate 50 new memory slots for materials if necessary
		if ( (m_nNumMaterials%50) == 0 ) 
		{
			n = (m_nNumMaterials+50)*sizeof(SAGAMATERIAL);
			m_pMaterials = (SAGAMATERIAL*)realloc(m_pMaterials, n);
			if (!m_pMaterials) return SAGA_OUTOFMEMORY;
		}
		memcpy(&m_pMaterials[m_nNumMaterials], &mat, sizeof(SAGAMATERIAL));
		m_nNumMaterials++;
	}

	return SAGA_OK;
} 
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: CreateTexture
//	Purpose:
//		Creates a d3dtexture object and loads the image data from disc.
//		Parameters:
//			SAGATEXTURE - reference to texture object to store data
HRESULT SAGAD3DSkinManager::CreateTexture(SAGATEXTURE *pTexture, bool bAlpha) 
{
	D3DLOCKED_RECT     d3dRect;
	D3DFORMAT          fmt;
	DIBSECTION         dibS;
	HRESULT            hr;
	int                LineWidth;
	void              *pMemory=NULL;

	HBITMAP hBMP = (HBITMAP)LoadImage(NULL, pTexture->chName, IMAGE_BITMAP,0,0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
	if (!hBMP) 
	{
		Log("Error: cannot open texture \"%s\"", pTexture->chName);
		return SAGA_FILENOTFOUND;
	}

	GetObject(hBMP, sizeof(DIBSECTION), &dibS);

	//	We support only 24 bit bitmaps
	if (dibS.dsBmih.biBitCount != 24) 
	{
		DeleteObject(hBMP);
		Log("Error: texture is not 24 bit \"%s\"", pTexture->chName);
		return SAGA_INVALIDFILE;
	}

	if (bAlpha) fmt = D3DFMT_A8R8G8B8;
	else fmt = D3DFMT_R5G6B5;

	long  lWidth   = dibS.dsBmih.biWidth;
	long  lHeight  = dibS.dsBmih.biHeight;
	BYTE *pBMPBits = (BYTE*)dibS.dsBm.bmBits;

	//	Build D3D texture object
	hr = m_pDevice->CreateTexture(	lWidth, lHeight, 1, 0, fmt, D3DPOOL_MANAGED, 
									(LPDIRECT3DTEXTURE9*)(&(pTexture->pData)), NULL);
	if (FAILED(hr)) 
	{
		if (hr==D3DERR_INVALIDCALL)
			Log("Error: IDirect3DDevice::CreateTexture failed (D3DERR_INVALIDCALL)");
		else if (hr==D3DERR_OUTOFVIDEOMEMORY)
			Log("Error: IDirect3DDevice::CreateTexture failed (D3DERR_OUTOFVIDEOMEMORY)");
		else if (hr==E_OUTOFMEMORY)
			Log("Error: IDirect3DDevice::CreateTexture failed (E_OUTOFMEMORY)");
		else if (hr==D3DOK_NOAUTOGEN)
			Log("Error: IDirect3DDevice::CreateTexture failed (D3DOK_NOAUTOGEN)");
		else
			Log("Error: IDirect3DDevice::CreateTexture failed (unknown)");

		return SAGA_FAIL;
	}

	//	Get a dummy pointer
	LPDIRECT3DTEXTURE9 pTex = ((LPDIRECT3DTEXTURE9)pTexture->pData);

	if (FAILED(pTex->LockRect(0, &d3dRect, NULL, 0))) 
	{
		Log("Error: cannot lock texture to copy pixels \"%s\"", pTexture->chName);
		return SAGA_BUFFERLOCK;
	}

	if (bAlpha) 
	{
		LineWidth = d3dRect.Pitch >> 2; // 32 bit = 4 byte
		pMemory = (DWORD*)d3dRect.pBits;
	}
	else 
	{
		LineWidth = d3dRect.Pitch >> 1; // 16 bit = 2 Byte
		pMemory = (USHORT*)d3dRect.pBits;
	}

	//	Copy each pixel:
	//		the magic 3 in the formulars is given by the fact
	//      that each pixel in the BMP is 3 byte (24 bit)
	for (int cy = 0; cy < lHeight; cy++) 
	{
		for (int cx = 0; cx < lWidth; cx++) 
		{
			if (bAlpha) 
			{
				DWORD Color = 0xff000000;
				int   i = (cy*lWidth + cx)*3;
				memcpy(&Color, &pBMPBits[i], sizeof(BYTE)*3);
				((DWORD*)pMemory)[cx + (cy*LineWidth)] = Color;
			} //	32 Bit
			else 
			{
				//	Convert each 24 bit pixel value to 16 bit
				UCHAR B = (pBMPBits[(cy*lWidth + cx)*3 + 0]) >> 3,
					G = (pBMPBits[(cy*lWidth + cx)*3 + 1]) >> 3,
					R = (pBMPBits[(cy*lWidth + cx)*3 + 2]) >> 3;

				USHORT Color = RGB16BIT( (int)(((float) R / 255.0f) * 32.0f),
					(int)(((float) G / 255.0f) * 64.0f),
					(int)(((float) B / 255.0f) * 32.0f) );
				//	Put pixel as 16 Bit color
				((USHORT*)pMemory)[cx + (cy*LineWidth)] = Color;
			} //	16 Bit

		} 
	} 

	pTex->UnlockRect(0);
	DeleteObject(hBMP);
	return SAGA_OK;
} 
//
////////////////////////////////////////


////////////////////////////////////////
//	Function Name: VectortoRGBA
//	Purpose:
//		Encode vector data as RGBA color value for normal map
DWORD VectortoRGBA(SAGAVector *vc, float fHeight) 
{
	DWORD r = (DWORD)( 127.0f * vc->x + 128.0f );
	DWORD g = (DWORD)( 127.0f * vc->y + 128.0f );
	DWORD b = (DWORD)( 127.0f * vc->z + 128.0f );
	DWORD a = (DWORD)( 255.0f * fHeight );
	return( (a<<24L) + (r<<16L) + (g<<8L) + (b<<0L) );
}
//
////////////////////////////////////////


////////////////////////////////////////
//	Function Name:
//	Purpose:
//		Suppose the texture is 32 Bit format HeightMap and needs to be
//		converted to be a normal map instead.
HRESULT SAGAD3DSkinManager::ConvertToNormalmap(SAGATEXTURE *pTexture) 
{
	HRESULT hr=SAGA_OK;

	D3DLOCKED_RECT  d3dRect;
	D3DSURFACE_DESC desc;

	// get a dummy pointer
	LPDIRECT3DTEXTURE9 pTex = ((LPDIRECT3DTEXTURE9)pTexture->pData);

	pTex->GetLevelDesc(0, &desc);

	if (FAILED(pTex->LockRect(0, &d3dRect, NULL, 0))) 
	{
		Log("Error: cannot lock texture to copy pixels \"%s\"", pTexture->chName);
		return SAGA_BUFFERLOCK;
	}

	//	Get pointer to pixel data
	DWORD* pPixel = (DWORD*)d3dRect.pBits;

	//	Build normals at each pixel
	for (DWORD j=0; j<desc.Height; j++) 
	{
		for (DWORD i=0; i<desc.Width; i++) 
		{
			DWORD color00 = pPixel[0];
			DWORD color10 = pPixel[1];
			DWORD color01 = pPixel[d3dRect.Pitch/sizeof(DWORD)];

			float fHeight00 = (float)((color00&0x00ff0000)>>16)/255.0f;
			float fHeight10 = (float)((color10&0x00ff0000)>>16)/255.0f;
			float fHeight01 = (float)((color01&0x00ff0000)>>16)/255.0f;

			ZFXVector vcPoint00( i+0.0f, j+0.0f, fHeight00 );
			ZFXVector vcPoint10( i+1.0f, j+0.0f, fHeight10 );
			ZFXVector vcPoint01( i+0.0f, j+1.0f, fHeight01 );
			ZFXVector vc10 = vcPoint10 - vcPoint00;
			ZFXVector vc01 = vcPoint01 - vcPoint00;

			ZFXVector vcNormal;
			vcNormal.Cross(vc10, vc01);
			vcNormal.Normalize();

			//	Store normal as RGBA in NormalMap
			*pPixel++ = VectortoRGBA( &vcNormal, fHeight00 );
		}
	}
	pTex->UnlockRect(0);

	LPDIRECT3DSURFACE9 pSurface=NULL;
	pTex->GetSurfaceLevel(0, &pSurface);
	D3DXSaveSurfaceToFile("normal.bmp", D3DXIFF_BMP, pSurface, NULL, NULL);

	return SAGA_OK;
} 
//
////////////////////////////////////////


////////////////////////////////////////
//	Function Name: MakeD3DColor
//	Purpose:
//		Calculates a 32 bit ARGB value for use with d3d from given RGBA
//		color values ranging from 0-255.
//		Parameters:
//			UCHAR - red value
//			UCHAR - green value
//			UCHAR - blue value
//			UCHAR - alpha value
DWORD SAGAD3DSkinManager::MakeD3DColor(UCHAR R, UCHAR G, UCHAR B, UCHAR A) 
{ 
	DWORD Color;

	DWORD RBitMask = 0x00ff0000;
	DWORD GBitMask = 0x0000ff00;
	DWORD BBitMask = 0x000000ff;
	DWORD ABitMask = 0xff000000;

	//	Scale down to 0.0f-1.0f values
	float fA = A/255.0f;
	float fR = R/255.0f;
	float fG = G/255.0f;
	float fB = B/255.0f;

	//	Scale down 100% a,r,g,b values and cut to their bit range
	Color = ( ((DWORD)(ABitMask * fA)) & ABitMask ) +
		( ((DWORD)(RBitMask * fR)) & RBitMask ) +
		( ((DWORD)(GBitMask * fG)) & GBitMask ) +
		( ((DWORD)(BBitMask * fB)) & BBitMask );

	return Color; 
} 
//
////////////////////////////////////////


////////////////////////////////////////
//	Function Name: SetAlphaKey
//	Purpose:
//		Sets the alpha channel of all pixels with given RGB value to the
//		amount of transparency specified. Make sure the corresponding
//		RGB values in the texture have alpha value set to 1.0f. So set
//		all alpha key prior to calling SetTransparency().
//		Parameters:
//			UCHAR              - red (0-255)
//			UCHAR              - green (0-255)
//			UCHAR              - blue (0-255)
//			UCHAR              - alpha (0-255)
//		Returns:
//			LPDIRECT3DTEXTURE9 - changed texture
HRESULT SAGAD3DSkinManager::SetAlphaKey(LPDIRECT3DTEXTURE9 *ppTexture,
										UCHAR R, UCHAR G, UCHAR B, UCHAR A) 
{
	D3DSURFACE_DESC  d3dDesc;
	D3DLOCKED_RECT   d3dRect;
	DWORD            dwKey, Color;

	//	Security check: must be ARGB format
	(*ppTexture)->GetLevelDesc(0, &d3dDesc);
	if (d3dDesc.Format != D3DFMT_A8R8G8B8)
	   return SAGA_INVALIDPARAM;

	//	Calculate the key value
	dwKey = MakeD3DColor(R, G, B, 255);

	//	Calculate new color to set for key pixels
	if (A > 0) Color = MakeD3DColor(R, G, B, A);
	else Color = MakeD3DColor(0, 0, 0, A);

	if (FAILED((*ppTexture)->LockRect(0, &d3dRect, NULL, 0)))
	   return SAGA_BUFFERLOCK;

	//	Overwrite all key pixels with new color value
	for (DWORD y=0; y<d3dDesc.Height; y++) 
	{
	   for (DWORD x=0; x<d3dDesc.Width; x++) 
	   {
		   if ( ((DWORD*)d3dRect.pBits)[d3dDesc.Width*y+x] == dwKey )
			   ((DWORD*)d3dRect.pBits)[d3dDesc.Width*y+x] = Color;
	   }
	}
	(*ppTexture)->UnlockRect(0);

	return SAGA_OK;
} 
//
////////////////////////////////////////


////////////////////////////////////////
//	Function Name: SetTransparency
//	Purpose:
//		Sets all pixels in the texture to the amount of transparency.
//		Parameters:
//			LPDIRECT3DTEXTURE9 - changed texture
//		Returns:
//			UCHAR              - alpha value (0-255)
HRESULT SAGAD3DSkinManager::SetTransparency(LPDIRECT3DTEXTURE9 *ppTexture, UCHAR Alpha) 
{
	D3DSURFACE_DESC  d3dDesc;
	D3DLOCKED_RECT   d3dRect;
	DWORD            Color;
	UCHAR            A, R, G, B;

	//	Security check: must be ARGB format
	(*ppTexture)->GetLevelDesc(0, &d3dDesc);
	if (d3dDesc.Format != D3DFMT_A8R8G8B8)
	   return SAGA_INVALIDPARAM;

	if (FAILED((*ppTexture)->LockRect(0, &d3dRect, NULL, 0)))
	   return SAGA_BUFFERLOCK;

	//	Loop through all pixels
	for (DWORD y=0; y<d3dDesc.Height; y++) 
	{
		for (DWORD x=0; x<d3dDesc.Width; x++) 
		{
			//	Get color value from this pixel
			Color = ((DWORD*)d3dRect.pBits)[d3dDesc.Width*y+x];

			//	Calculate ARGB values from pixel color
			A = (UCHAR)( (Color & 0xff000000) >> 24);
			R = (UCHAR)( (Color & 0x00ff0000) >> 16);
			G = (UCHAR)( (Color & 0x0000ff00) >> 8);
			B = (UCHAR)( (Color & 0x000000ff) >> 0);

			//	Only set new alpha value if old value is greater
			if (A >= Alpha)
			   A = Alpha;

			//	Put new color value for this pixel
			((DWORD*)d3dRect.pBits)[d3dDesc.Width*y+x] = 
			   MakeD3DColor(R, G, B, A);
		}
	}
	(*ppTexture)->UnlockRect(0);

	return SAGA_OK;
} 
//
////////////////////////////////////////

////////////////////////////////////////
//	Function Name: Log
//	Purpose:
//		write Output string to attribute Output stream if exists
//		Parameters:
//			bool - flush immediately
//			char - format string to output
//			...  - output values
void SAGAD3DSkinManager::Log(char *chString, ...) 
{
	char ch[256];
	char *pArgs;

	pArgs = (char*) &chString + sizeof(chString);
	vsprintf(ch, chString, pArgs);
	fprintf(m_pLog, "[SAGAD3DSkinMan]: ");
	fprintf(m_pLog, ch);
	fprintf(m_pLog, "\n");

	if (g_bLF)
		fflush(m_pLog);
} 
//
////////////////////////////////////////


////////////////////////////////////////
//	Function Name: LogCurrentStatus
//	Purpose:
//		Used to log values e.g. how many skins, testures, materials loaded.
void SAGAD3DSkinManager::LogCurrentStatus(char *chLog, bool bDetailed) 
{
	FILE *pLog = fopen(chLog, "w");

	fprintf(pLog, "\n\nSKINMANAGER_SITREP: skins: %d, mats: %d, textures: %d \n", 
		m_nNumSkins, m_nNumMaterials, m_nNumTextures);

	if (!bDetailed) return;

	fprintf(pLog, "SKINS { \n");
	for (UINT i=0; i<m_nNumSkins; i++) {
		fprintf(pLog, "   ID: %d { matriall: %d, ", i, m_pSkins[i].nMaterial);
		fprintf(pLog, "texture: [%d,%d,%d,%d,%d,%d,%d,%d] }\n", 
			m_pSkins[i].nTexture[0], m_pSkins[i].nTexture[1],
			m_pSkins[i].nTexture[2], m_pSkins[i].nTexture[3],
			m_pSkins[i].nTexture[4], m_pSkins[i].nTexture[5],
			m_pSkins[i].nTexture[6], m_pSkins[i].nTexture[7]);
	}
	fprintf(pLog, "   } \n");

	fprintf(pLog, "TEXTURES { \n");
	for (UINT j=0; j<m_nNumTextures; j++) 
	{
		fprintf(pLog, "   ID: %d {\"%s\"} \n", j, m_pTextures[j].chName);
	}
	fprintf(pLog, "   } \n\n");

	fprintf(pLog, "MATERIALS { \n");
	for (UINT k=0; k<m_nNumMaterials; k++) 
	{
		fprintf(pLog, "   ID: %d\n", k);
		fprintf(pLog, "   Diffuse (%f,%f,%f,%f)\n", m_pMaterials[k].cDiffuse.c[0],
			m_pMaterials[k].cDiffuse.c[1], m_pMaterials[k].cDiffuse.c[2], 
			m_pMaterials[k].cDiffuse.c[3]);
		fprintf(pLog, "   Ambient (%f,%f,%f,%f)\n", m_pMaterials[k].cAmbient.c[0],
			m_pMaterials[k].cAmbient.c[1], m_pMaterials[k].cAmbient.c[2], 
			m_pMaterials[k].cAmbient.c[3]);
		fprintf(pLog, "   Emissive (%f,%f,%f,%f)\n", m_pMaterials[k].cEmissive.c[0],
			m_pMaterials[k].cEmissive.c[1], m_pMaterials[k].cEmissive.c[2], 
			m_pMaterials[k].cEmissive.c[3]);
		fprintf(pLog, "   Specular (%f,%f,%f,%f) power: %f\n", m_pMaterials[k].cSpecular.c[0],
			m_pMaterials[k].cSpecular.c[1], m_pMaterials[k].cSpecular.c[2], 
			m_pMaterials[k].cSpecular.c[3], m_pMaterials[k].fPower);
	}
	fprintf(pLog, "   } \n\n");

	fclose(pLog);
}
//
////////////////////////////////////////